package com.framework.core.dispacher;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.framework.core.beanfactory.MyBeanFactory;
import com.framework.core.domain.BaseControl;
import com.framework.core.domain.Control;
import com.framework.core.utils.PropertiesUtils;
import com.framework.core.utils.StringUtils;

import javassist.CannotCompileException;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

/**
 * request调度器
 * 
 * @author Rose 2017年9月13日
 */
public class ControlDispatcher {

	private HttpServletRequest req;
	private HttpServletResponse resp;
	private ServletContext sec;

	private final static String prefixString = "jsp.view.prefix";
	private final static String suffixString = "jsp.view.suffix";
	private final static String errJspPath = "jsp.err.path";

	public final static String prefix;
	public final static String suffix;
	public final static String errPath;

	// 包装类型记录
	// private final String[] objTypes = { Integer.class.getName(),
	// Long.class.getName(), Byte.class.getName(),
	// Short.class.getName(), Float.class.getName(), Double.class.getName() };
	// 基本类型记录
	// private final String[] baseTypes = { int.class.getName(),
	// long.class.getName(), byte.class.getName(),
	// short.class.getName(), float.class.getName(), double.class.getName() };

	static {
		// 初始化返回路径前后缀
		prefix = PropertiesUtils.getPropertiesValueByName(prefixString);
		suffix = PropertiesUtils.getPropertiesValueByName(suffixString);
		errPath = PropertiesUtils.getPropertiesValueByName(errJspPath);
	}

	public ControlDispatcher(HttpServletRequest req, HttpServletResponse resp, ServletContext sec) {
		this.req = req;
		this.resp = resp;
		this.sec = sec;
	}

	public void builder(Control control) throws ServletException, IOException {
		String clazzName = control.getClazz();
		String method = control.getMethod();
		try {

			Class<?>[] prametersClassType = getPrametersClassType(clazzName, method);
			Class<?> clazz = Class.forName(clazzName);

			// 创建对象
			// Object obj = clazz.newInstance();
			// 从ioc获取,单例
			String s = StringUtils.toLowerCaseFirstOne(clazz.getSimpleName());
			Object obj = MyBeanFactory.getIocBean(s);

			// 保存请求域,给继承基类的control用
			// 判断是否继承基类
			if (BaseControl.class.isAssignableFrom(clazz)) {
				System.out.println("继承基类！");
				setScope(clazz, obj);
			} else {
				System.out.println("非继承基类！");
			}
			Method mt = clazz.getMethod(method, prametersClassType);
			Object[] params = getPrametersName(clazzName, method);

			// 通过反射目标 调用方法
			String returnValue = mt.invoke(obj, params).toString();
			// 最终路径
			String returnPath = "";
			if (!prefix.isEmpty() && !suffix.isEmpty()) {
				// 拼接返回路径
				returnPath = prefix + returnValue + suffix;
				// 根据结果跳转页码
				req.getRequestDispatcher(returnPath).forward(req, resp);
			}

			// resp.getWriter().write(returnValue.toString());

		} catch (Exception e) {
			e.printStackTrace();
			if (!prefix.isEmpty() && !suffix.isEmpty() && !StringUtils.isEmpty(errPath)) {
				// 拼接返回路径
				String err = prefix + errPath + suffix;
				// 跳转异常页面
				req.setAttribute("errMsg", e.getMessage());
				req.setAttribute("test", "出现反射异常！");
				req.getRequestDispatcher(err).forward(req, resp);
			} else {
				System.err.println("没有在Properties配置好jsp返回映射路径！");
			}
		}
	}

	// 使用javassist的反射方法获取方法的参数名
	public Object[] getPrametersName(String clazzName, String method) {
		Object[] params = null;
		try {

			ClassPool pool = ClassPool.getDefault();
			// 解决classnofound异常
			ClassClassPath classPath = new ClassClassPath(this.getClass());
			pool.insertClassPath(classPath);

			CtClass cc = pool.get(clazzName);
			CtMethod ctMethod = cc.getDeclaredMethod(method);

			MethodInfo methodInfo = ctMethod.getMethodInfo();
			CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
			LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute
					.getAttribute(LocalVariableAttribute.tag);
			if (attr == null) {
				// exception
			}

			CtClass[] parameterTypes = ctMethod.getParameterTypes();
			params = new Object[parameterTypes.length];
			int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1;

			// paramNames即参数名
			for (int i = 0; i < params.length; i++) {
				// params[i] = attr.variableName(i + pos);
				// 从请求域获取参数并进行类型转换(包装类型)
				if (Integer.class.getName().equals(parameterTypes[i].getName())) {
					String cs = req.getParameter(attr.variableName(i + pos));
					if (StringUtils.isNotEmpty(cs)) {
						params[i] = Integer.parseInt(cs);
					} else {
						System.err.println("参数：" + attr.variableName(i + pos) + "为空！");
					}
					continue;
				}
				if (Float.class.getName().equals(parameterTypes[i].getName())) {
					String cs = req.getParameter(attr.variableName(i + pos));
					if (StringUtils.isNotEmpty(cs)) {
						params[i] = Float.parseFloat(cs);
					} else {
						System.err.println("参数：" + attr.variableName(i + pos) + "为空！");
					}
					continue;
				}
				if (Short.class.getName().equals(parameterTypes[i].getName())) {
					String cs = req.getParameter(attr.variableName(i + pos));
					if (StringUtils.isNotEmpty(cs)) {
						params[i] = Short.parseShort(cs);
					} else {
						System.err.println("参数：" + attr.variableName(i + pos) + "为空！");
					}
					continue;
				}
				if (Double.class.getName().equals(parameterTypes[i].getName())) {
					String cs = req.getParameter(attr.variableName(i + pos));
					if (StringUtils.isNotEmpty(cs)) {
						params[i] = Double.parseDouble(cs);
					} else {
						System.err.println("参数：" + attr.variableName(i + pos) + "为空！");
					}
					continue;
				}
				if (Date.class.getName().equals(parameterTypes[i].getName())) {
					SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
					String cs = req.getParameter(attr.variableName(i + pos));
					if (StringUtils.isNotEmpty(cs)) {
						try {
							params[i] = sdf.parse(cs);
						} catch (ParseException e) {
							e.printStackTrace();
						}
					} else {
						System.err.println("参数：" + attr.variableName(i + pos) + "为空！");
					}
				}
				// 基础类型
				params[i] = req.getParameter(attr.variableName(i + pos));

			}

		} catch (NotFoundException e) {
			e.printStackTrace();
		}
		return params;
	}

	// 获取请求方法参数类型
	public Class<?>[] getPrametersClassType(String clazzName, String method) throws CannotCompileException {
		Class<?>[] parameterTypes = null;
		try {
			Class<?> clazz = Class.forName(clazzName);
			Method[] declaredMethods = clazz.getDeclaredMethods();
			for (Method m : declaredMethods) {
				if (method.equals(m.getName())) {
					parameterTypes = m.getParameterTypes();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return parameterTypes;
	}

	// 请求域保存,方便继承basecontrol父类的的子类获取请求域
	private void setScope(Class<?> clazz, Object obj) throws NoSuchMethodException, SecurityException,
			IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		// 通过set放去注入field值
		String setRequest = "setRequest";
		String setResponse = "setResponse";
		String setServletContext = "setServletContext";
		Method requestMethod = clazz.getMethod(setRequest, HttpServletRequest.class);
		requestMethod.invoke(obj, req);
		Method reponseMethod = clazz.getMethod(setResponse, HttpServletResponse.class);
		reponseMethod.invoke(obj, resp);
		Method servletContextMethod = clazz.getMethod(setServletContext, ServletContext.class);
		servletContextMethod.invoke(obj, sec);
	}

	// 依赖注入
	public static void setTargetBean(Class<?> clazz, String targetAttr, Object obj, Object targetBean)
			throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
			InvocationTargetException, NoSuchFieldException {
		System.err.println("正在注入----类：" + targetBean.getClass() + "---属性："+targetAttr);
		// 不通过set方法注入field值
		Field field = clazz.getDeclaredField(targetAttr);
		field.setAccessible(true);
		field.set(obj, targetBean);
	}

	// 通过set依赖注入
	public static void setTargetBeanBySet(Class<?> clazz, Class<?> targetClass, Object obj, Object targetBean)
			throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
			InvocationTargetException, NoSuchFieldException {
		System.err.println("正在注入-------" + targetClass.getSimpleName());
		String requestMethodName = "setLoginRigisterMapper";
		Method method = clazz.getMethod(requestMethodName, targetClass);
		method.invoke(obj, targetBean);
	}
	// 获取方法注解
	// if (mt.isAnnotationPresent(MyRequestMapping.class)) {
	// MyRequestMapping annotation =
	// mt.getAnnotation(MyRequestMapping.class);
	// } else {
	// System.err.println("此方法未添加@" + MyController.class.getSimpleName()
	// + "注解！");
	// // throw new NeverAddAnnotationException("此方法未添加@" +
	// // MyController.class.getSimpleName() + "注解！");
	// }

	// 判断是否需要依赖注入
	// Field[] declaredFields = clazz.getDeclaredFields();
	// for (Field field : declaredFields) {
	// if (field.isAnnotationPresent(MyAutowire.class)) {
	// System.out.println(field.getName() + "添加了MyAutowire注解！");
	// // 分隔
	// String[] split =
	// field.getGenericType().getTypeName().split("\\.");
	// // beanName
	// String beanName =
	// StringUtils.toLowerCaseFirstOne(split[split.length - 1]);
	// // 需要注入的bean
	// Object targetBean = MyBeanFactory.getBeanByName(beanName);
	//
	// setTargetBean(clazz, beanName, obj, targetBean);
	//
	// }
	// }

	// 获取类注解
	// if (clazz.isAnnotationPresent(MyController.class)) {
	// MyController annotation =
	// clazz.getAnnotation(MyController.class);
	// } else {
	// System.err.println("此类未添加@" + MyController.class.getSimpleName()
	// + "注解！");
	// throw new NeverAddAnnotationException("此类未添加@" +
	// MyController.class.getSimpleName() + "注解！");
	// }
}
